package com.nowcoder.topic.greedy.hard;

import java.util.ArrayList;

/**
 * NC347 分割数组
 * @author d3y1
 */
public class NC347{
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param num int整型ArrayList
     * @param m int整型
     * @return int整型
     */
    public int splitMin (ArrayList<Integer> num, int m) {
        // return solution1(num, m);
        return solution2(num, m);
    }

    /**
     * 动态规划
     *
     * dp[i][j]表示数组num前i个数分成j个非空连续子数组的最小的最大和(非空连续子数组)
     *
     * dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j-1], sum))  , 1<=i<=n && 1<=j<=min(i,m) && j-1<=k<=i-1
     *
     * @param num
     * @param m
     * @return
     */
    private int solution1(ArrayList<Integer> num, int m){
        int n = num.size();

        int[][] dp = new int[n+1][m+1];

        // 前i个数
        for(int i=1; i<=n; i++){
            // 分成j个非空连续子数组
            for(int j=1; j<=i&&j<=m; j++){
                dp[i][j] = Integer.MAX_VALUE;
                int sum = 0;
                // 前k个数分成j-1个非空连续子数组
                for(int k=i-1; k>=j-1; k--){
                    // sum = num[k+1,i] -> 第k+1到第i个数的和
                    sum += num.get(k);
                    // dp[k][j-1]: 数组num前k个数分成0个非空连续子数组 -> 无法实现
                    if(k>0 && j-1==0){
                        continue;
                    }
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j-1], sum));
                }
            }
        }

        return dp[n][m];
    }

    /**
     * 二分 + 贪心
     * @param num
     * @param m
     * @return
     */
    private int solution2(ArrayList<Integer> num, int m){
        int n = num.size();
        int max=0, sum=0;
        int val;
        for(int i=0; i<n; i++){
            val = num.get(i);
            max = Math.max(max, val);
            sum += val;
        }

        int left = max;
        int right = sum;
        int mid;
        // 二分
        while(left <= right){
            mid = (left+right)/2;
            int cnt=1;
            int part = 0;
            for(int curr: num){
                // 贪心
                if(part+curr > mid){
                    cnt++;
                    part = curr;
                }else{
                    part += curr;
                }
            }
            // left最终指向第一个小于等于m的位置
            if(cnt > m){
                left = mid+1;
            }else{
                right = mid-1;
            }
        }

        return left;
    }
}